//
//  LGSName.m
//  LogicSim
//
//  Created by Jeremy Pereira on 26/02/2013.
//
//

#import "LGSName.h"
#import "LGSNode.h"

@interface LGSName()

@property (assign) int nodeIndex;
@property (assign) int connectionIndex;

+(int) parseIndexFrom: (NSString*) aString
      remainingString: (NSString* __autoreleasing*) remaining;

@end

@implementation LGSName
{
    NSString* nodePart_;
    NSString* connectionPart_;
    NSUInteger hashValue;
}

@synthesize nodePart = nodePart_, connectionPart = connectionPart_;

-(id) initWithNodePart: (NSString*) nodePart
		connectionPart: (NSString*) connectionPart
			 nodeIndex: (int) nodeIndex
       connectionIndex: (int) connectionIndex
{
    self = [super init];
    if (self != nil)
    {
        nodePart_ = [nodePart copy];
        connectionPart_ = [connectionPart copy];
        [self setNodeIndex: nodeIndex];
        [self setConnectionIndex: connectionIndex];
        hashValue = [nodePart_ hash]
        			+ [connectionPart_ hash]
        			+ (nodeIndex == LGS_NAME_NO_INDEX ? 0 : nodeIndex)
        			+ (connectionIndex == LGS_NAME_NO_INDEX ? 0 : connectionIndex);
    }
    return self;
}

+(LGSName*) nameWithString: (NSString*)aString
{
    LGSName* ret = nil;
    NSArray* parts = [aString splitIntoNodeAndConnection];
    NSString* nodePart = nil;
    NSString* connectionPart = nil;
    if (parts == nil)
    {
        nodePart = aString;
    }
    else
    {
        nodePart = [parts objectAtIndex: 0];
        connectionPart = [parts objectAtIndex: 1];
    }
    NSString* nodePartNoIndex = nil;
    NSString* connectionPartNoIndex = nil;
    int nodeIndex = [self parseIndexFrom: nodePart
                         remainingString: &nodePartNoIndex ];
    int connectionIndex = [self parseIndexFrom: connectionPart
                               remainingString: &connectionPartNoIndex ];
    ret = [[LGSName alloc] initWithNodePart: nodePartNoIndex
                             connectionPart: connectionPartNoIndex
                                  nodeIndex: nodeIndex
                            connectionIndex: connectionIndex];
    return ret;
}


+(LGSName*) nameWithConnectionString: (NSString*) connectionPart
{
    LGSName* ret = nil;
    NSString* connectionPartNoIndex = nil;
    int connectionIndex = [self parseIndexFrom: connectionPart
                               remainingString: &connectionPartNoIndex ];
    ret = [[LGSName alloc] initWithNodePart: nil
                             connectionPart: connectionPartNoIndex
                                  nodeIndex: LGS_NAME_NO_INDEX
                            connectionIndex: connectionIndex];
    return ret;
}


+(int) parseIndexFrom: (NSString*) aString
      remainingString: (NSString* __autoreleasing*) remaining
{
    int ret = LGS_NAME_NO_INDEX;
    if (aString == nil)
    {
        if (remaining != nil)
        {
            *remaining = nil;
        }
    }
    else
    {
        NSArray* split = [aString componentsSeparatedByString: @"["];
        /*
         *  The string must be in the form name[number].  If not, we assume
         *  it's just a name.
         */
        if ([split count] == 2)
        {
            if ([[split objectAtIndex: 1] length] > 1
            	&& [[split objectAtIndex: 1] hasSuffix: @"]"])
            {
                NSScanner* scanner = [NSScanner scannerWithString: [split objectAtIndex: 1]];
                int scannedIndex = 0;
                if ([scanner scanInt: &scannedIndex]
                    && scannedIndex != INT_MAX
                    && scannedIndex >= 0)
                {
                    ret = scannedIndex;
                }
            }
            if (remaining != nil)
            {
                *remaining = [split objectAtIndex: 0];
            }
        }
        else if (remaining != nil)
        {
            *remaining = aString;
        }
    }
    return ret;
}


-(NSUInteger) hash
{
    return hashValue;
}

-(BOOL) isEqual: (id)object
{
    BOOL ret = NO;
    if ([object isKindOfClass: [LGSName class]])
    {
        ret = self == object;
        if (!ret)
        {
            if ([self hash] == [object hash])
            {
                int selfNodeIndex = [self nodeIndex];
                int otherNodeIndex = [object nodeIndex];
                if (selfNodeIndex == LGS_NAME_NO_INDEX)
                {
                    selfNodeIndex = 0;
                }
                if (otherNodeIndex == LGS_NAME_NO_INDEX)
                {
                    otherNodeIndex = 0;
                }
                int selfConnectionIndex = [self connectionIndex];
                int otherConnectionIndex = [object connectionIndex];
                if (selfConnectionIndex == LGS_NAME_NO_INDEX)
                {
                    selfConnectionIndex = 0;
                }
                if (otherConnectionIndex == LGS_NAME_NO_INDEX)
                {
                    otherConnectionIndex = 0;
                }

                NSString* selfNode = [self nodePart];
                NSString* objectNode = [object nodePart];
                NSString* selfConnection = [self connectionPart];
                NSString* objectConnection = [object connectionPart];
                ret = (selfNode == objectNode || [selfNode isEqualToString: objectNode])
                      && (selfConnection == objectConnection // takes care of both being nil
                          || [selfConnection isEqualToString: objectConnection])
                      && selfNodeIndex == otherNodeIndex
                	  && selfConnectionIndex == otherConnectionIndex;
            }
        }
    }
    return ret;
}

-(id) copyWithZone: (NSZone*) zone
{
    return self;
}

-(LGSName*) nameWithConnectionString: (NSString*) connection
{
    LGSName* ret = self;
    if (connection != connectionPart_
        && ![connection isEqualToString: connectionPart_])
    {
        ret = [[LGSName alloc] initWithNodePart: nodePart_
                                 connectionPart: connection
                                      nodeIndex: [self nodeIndex]
                                connectionIndex: LGS_NAME_NO_INDEX];
    }
    return ret;
}

-(LGSName*) nameWithConnection: (LGSName*) connection
{
    LGSName* ret = [[LGSName alloc] initWithNodePart: nodePart_
                                 	  connectionPart: [connection connectionPart]
                                           nodeIndex: [self nodeIndex]
                                	 connectionIndex: [connection connectionIndex]];
    return ret;
}

-(LGSName*) nameWithNodeIndex: (int) nodeIndex
{
    LGSName* ret = self;
    if (nodeIndex != [self nodeIndex])
    {
        ret = [[LGSName alloc] initWithNodePart: nodePart_
                                 connectionPart: connectionPart_
                                      nodeIndex: nodeIndex
                                connectionIndex: [self connectionIndex]];
    }
    return ret;
}

-(LGSName*) nameWithConnectionIndex: (int) connectionIndex
{
    LGSName* ret = self;
    if (connectionIndex != [self connectionIndex])
    {
        ret = [[LGSName alloc] initWithNodePart: nodePart_
                                 connectionPart: connectionPart_
                                      nodeIndex: [self nodeIndex]
                                connectionIndex: connectionIndex];
    }
    return ret;
}

-(LGSName*) nameWithoutNodeIndex
{
    return [self nameWithNodeIndex: LGS_NAME_NO_INDEX];
}


-(NSString*) stringValue
{
    NSString* ret = nil;
    if ([self nodeIndex] == LGS_NAME_NO_INDEX)
    {
        ret = [self nodePart];
    }
    else
    {
        ret = [NSString stringWithFormat:
               @"%@[%d]",
               [self nodePart],
               [self nodeIndex]];
    }

    if ([self connectionPart] != nil)
    {
        if ([self connectionIndex] == LGS_NAME_NO_INDEX)
        {
            ret = [NSString stringWithFormat:
                   @"%@.%@",
                   ret,
                   [self connectionPart]];
        }
        else
        {
            ret = [NSString stringWithFormat:
                   @"%@.%@[%d]",
                   ret,
                   [self connectionPart],
                   [self connectionIndex]];
        }
    }
    return ret;
}

-(LGSName*) nameWithoutConnection
{
    LGSName* ret = self;
    if ([self connectionPart] != nil)
    {
        ret = [[[self class] alloc] initWithNodePart: [self nodePart]
                                      connectionPart: nil
                                           nodeIndex: [self nodeIndex]
                                     connectionIndex: LGS_NAME_NO_INDEX];
    }
    return ret;
}

-(LGSName*) nameWithoutNode
{
    LGSName* ret = self;
    if ([self nodePart] != nil)
    {
        ret = [[[self class] alloc] initWithNodePart: nil
                                      connectionPart: [self connectionPart]
                                           nodeIndex: LGS_NAME_NO_INDEX
                                     connectionIndex: [self connectionIndex]];
    }
    return ret;
}

#define COMPARISON_RESULT(a, b)	((a) == (b) ? NSOrderedSame	\
											: ((a) < (b) ? NSOrderedAscending \
														 : NSOrderedDescending))

-(NSComparisonResult) localizedCompare: (LGSName*) other
{
    NSComparisonResult ret = NSOrderedSame;
    ret = [[self nodePart] localizedCompare: [other nodePart]];
    if (ret == NSOrderedSame)
    {
        ret = COMPARISON_RESULT([self nodeIndex], [other nodeIndex]);
        if (ret == NSOrderedSame)
        {
            // TODO: connectionIndex comparison
            if ([self connectionPart] == nil)
            {
                ret = [other connectionPart] == nil ? NSOrderedSame : NSOrderedAscending;
            }
            else if ([other connectionPart] == nil)
            {
                ret = NSOrderedDescending;
            }
            else
            {
                ret = [[self  connectionPart] localizedCompare: [other connectionPart]];
            }
        }
    }
    return ret;
}

-(NSString*) description
{
    return [NSString stringWithFormat:
            @"<%@[%d],%@[%d]>",
            [self nodePart],
            [self nodeIndex],
            [self connectionPart],
            [self connectionIndex]];
}

+(LGSName*) null
{
    static dispatch_once_t onceToken;
    static LGSName* theNull = nil;
    dispatch_once(&onceToken, ^{
        theNull = [[LGSName alloc] initWithNodePart: nil
                                     connectionPart: nil
                                          nodeIndex: LGS_NAME_NO_INDEX
                                    connectionIndex: LGS_NAME_NO_INDEX];
    });
    return theNull;
}

@end


@implementation NSString (LGSName)

-(NSArray*) splitIntoNodeAndConnection
{
    NSArray* ret = nil;
    NSRange positionOfDot = [self rangeOfString: @"."
                                        options: NSBackwardsSearch];
    if (positionOfDot.location != NSNotFound)
    {
        NSString* name = [self substringToIndex:positionOfDot.location];
        NSString* connect = [self substringFromIndex:positionOfDot.location + 1];
		ret = [NSArray arrayWithObjects: name, connect, nil];
    }
    
	return ret;
}

-(LGSName*) asName
{
    return [LGSName nameWithString: self];
}

-(LGSName*) asConnectionName
{
    return [LGSName nameWithConnectionString: self];
}


@end


